import celery
import flask
import pagure
import requests
import urllib.parse

from .. import activitypub
from .. import model
from .. import settings
from . import broker
from . import broker_url
from . import database_session

log = celery.utils.log.get_task_logger(__name__)
log.setLevel(settings.LOG_LEVEL)

@broker.task
def handle_incoming_activity(project_id, activity):
    """
    A person has received a new Activity in its INBOX. This task is scheduled
    in order to triage the activity and decide how the actor should react to it.
    """
    
    activity = activitypub.Document(activity)
    
    with database_session() as (pagure_db, forgefed_db):
        
        project = pagure_db.query(model.Projects) \
                          .filter(model.Projects.id == project_id) \
                          .one_or_none()
        
        log.debug('Project {} now handling Activity {}'.format(project.local_uri, activity['id']))
        
        if not project:
            log.info('Actor id(' + project_id + ') doesn\'t exist. Incoming '
                     'Activity will be ignored.')
            return
        
        if activity['type'] == 'Create':
            """
            The Project has received a Create Activity.
            """
            
            # Dereference the actor
            # actor = activitypub.fetch(activity['actor'])
            
            # Retrieve the object of the Activity
            object = activity.node('object')
            
            if object['type'] == 'Note':
                log.debug('Somebody has Created a new Note.')
                
                # We're only interested in Notes that have a "context" property
                # because that contains the URI of the Ticket or MergeRequest
                if 'context' not in object:
                    log.debug('The Note does not contain a "context". Ignoring.')
                    return
                
                model.test_or_set_remote_comment(pagure_db, forgefed_db, object['id'])
        
        if activity['type'] == 'Follow':
            """
            Actor has received a Follow Aactivity. Somebody wants to follow this Project?
            """
            
            if activity['object'] != project.local_uri:
                log.info('Actor ' + project.local_uri + ' has received a Follow '
                         'request but the "object" is not this actor. '
                         'Activity will be ignored.')
                return
            
            # Check if the remote actor is already following the local actor
            if forgefed_db.query(
                   forgefed_db.query(database.Collection) \
                              .filter(database.Collection.uri == project.followers_uri) \
                              .filter(database.Collection.item == activity['actor']) \
                              .exists()
                ).scalar():
                
                log.info('Actor {} is already following {}. Will ignore Follow request.'.format(project.local_uri, activity['actor']))
                return
            
            # Add the remote object to our followers collection
            forgefed_db.add(database.Collection(uri = project.followers_uri, item = activity['actor']))
            
            # Commit before accepting the Activity because we want the new
            # follower in the Followers collection before sending out the
            # Accept Activity.
            forgefed_db.commit()
            
            # Automatically accept Follow requests.
            project.accept(activity)
        
        if activity['type'] == 'Offer':
            """
            Somebody is offering something to the Project, for example a Ticket.
            """
            
            # Retrieve the object of the Offer
            object = activity.node('object')
            
            if object['type'] == 'Ticket':
                log.debug('Request to create a new Ticket.')
                
                # Make sure we have a local actor representing the remote actor
                actor = model.test_or_set_remote_actor(pagure_db, forgefed_db, activity['actor'])
                
                # Create a new local issue in Pagure
                issue = pagure.lib.query.new_issue(
                    pagure_db,
                    project,
                    object['summary'],
                    object['content'],
                    actor.username)
                
                # Send an "Accept" request to notify the remote actor that the
                # ticket was created
                issue_uri = '{}/issue/{}'.format(project.local_uri, issue.id)
                
                project.accept(
                    activity,
                    to=activity['actor'],
                    result=issue_uri)
    
        if activity['type'] == 'Resolve' \
        and activity.node('object')['type'] == 'Ticket':
            """
            Somebody has resolved a Ticket.
            """
            
            # Retrieve the Ticket object
            ticket = model.from_local_uri(pagure_db, activity['object']['id'])
            
            if not ticket:
                log.debug('Ticket is not local: {}'.format(activity['object']['id']))
                return
            
            # Create a new object (or retrieve existing one) for the remote user
            actor = model.test_or_set_remote_actor(pagure_db, forgefed_db, activity['actor'])
            
            log.debug('Ticket resolved: {}'.format(activity['object']['id']))
            
            # Edit the pagure Issue object
            pagure.lib.query.edit_issue(
                session=pagure_db,
                issue=ticket,
                user=actor.username,
                status='Closed')
